package com.dragon.framework.exception;

import com.dragon.common.constant.HttpStatus;
import com.dragon.common.core.text.Convert;
import com.dragon.common.exception.ServiceException;
import com.dragon.common.utils.AjaxResult;
import com.dragon.common.utils.StringUtils;
import com.dragon.common.utils.html.EscapeUtil;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.validation.ConstraintViolationException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.BindException;
import org.springframework.web.HttpMediaTypeNotSupportedException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingPathVariableException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
import org.springframework.web.servlet.resource.NoResourceFoundException;

import java.nio.file.AccessDeniedException;
import java.util.Objects;

/**
 * 全局异常处理器
 *
 * @author dragon
 * @version 1.0
 * @date 2024/7/16 16:57
 */
@RestControllerAdvice
public class GlobalExceptionHandler {

    private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class);

    /**
     * 参数类型转换失败
     */
    public static final String PROPERTY_CONVERT_FAILED = "Failed to convert property value of type";

    /**
     * 请求body为空
     */
    public static final String MISSING_BODY = "Required request body is missing";

    /**
     * JSON解析错误
     */
    public static final String JSON_PARSE_ERROR = "JSON parse error";


    /**
     * 请求链接不存在
     */
    @ExceptionHandler(value = {NoResourceFoundException.class})
    public AjaxResult<Void> handleNoResourceFoundException(NoResourceFoundException e) {
        log.error("********** 全局异常捕捉[NoResourceFoundException] ********** {}", e.getMessage(), e);

        return AjaxResult.failed(HttpStatus.NOT_FOUND, "请求URL不可用");
    }

    /**
     * 请求方式不支持
     */
    @ExceptionHandler(value = HttpRequestMethodNotSupportedException.class)
    public AjaxResult<Void> handleHttpRequestMethodNotSupportedException(HttpRequestMethodNotSupportedException e, HttpServletRequest request) {
        log.error("********** 全局异常捕捉[HttpRequestMethodNotSupportedException] ********** {}", e.getMessage(), e);

        String requestURI = request.getRequestURI();
        log.error("请求地址'{}',不支持'{}'请求", requestURI, e.getMethod());

        return AjaxResult.failed(HttpStatus.ERROR, "请求方式错误");
    }

    /**
     * 请求参数为空
     */
    @ExceptionHandler(value = MissingServletRequestParameterException.class)
    public AjaxResult<Void> handleMissingServletRequestParameterException(MissingServletRequestParameterException e) {
        log.error("********** 全局异常捕捉[MissingServletRequestParameterException] ********** {}", e.getMessage(), e);

        return AjaxResult.failed(HttpStatus.ERROR, "请求参数为空");
    }

    /**
     * 请求参数为空
     */
    @ExceptionHandler(value = HttpMediaTypeNotSupportedException.class)
    public AjaxResult<Void> handleHttpMediaTypeNotSupportedException(HttpMediaTypeNotSupportedException e) {
        log.error("********** 全局异常捕捉[HttpMediaTypeNotSupportedException] ********** {}", e.getMessage(), e);

        return AjaxResult.failed(HttpStatus.ERROR, "请求参数为空");
    }

    /**
     * 请求参数为空
     */
    @ExceptionHandler(value = HttpMessageNotReadableException.class)
    public AjaxResult<Void> handleHttpMessageNotReadableException(HttpMessageNotReadableException e) {
        log.error("********** 全局异常捕捉[HttpMessageNotReadableException] ********** {}", e.getMessage(), e);

        String message = e.getMessage();
        if (StringUtils.isNotBlank(message)) {
            if (message.contains(MISSING_BODY)) {
                return AjaxResult.failed("请求参数为空");
            }
            if (message.contains(JSON_PARSE_ERROR)) {
                return AjaxResult.failed("JSON参数格式错误");
            }
        }

        return AjaxResult.failed(HttpStatus.ERROR, "请求参数为空");
    }

    /**
     * RequestParam参数注解校验
     */
    @ExceptionHandler(value = ConstraintViolationException.class)
    public AjaxResult<Void> handleConstraintViolationException(ConstraintViolationException e) {
        log.error("********** 全局异常捕捉[ConstraintViolationException] ********** {}", e.getMessage(), e);

        String message = e.getMessage().substring(e.getMessage().indexOf(":") + 1).trim();
        return AjaxResult.failed(HttpStatus.ERROR, message);
    }

    /**
     * RequestParam参数注解校验
     */
    @ExceptionHandler(value = MethodArgumentNotValidException.class)
    public AjaxResult<Void> handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
        log.error("********** 全局异常捕捉[MethodArgumentNotValidException] ********** {}", e.getMessage(), e);

        String message = e.getBindingResult().getAllErrors().get(0).getDefaultMessage();
        return AjaxResult.failed(HttpStatus.ERROR, message);
    }

    /**
     * 权限校验异常
     */
    @ExceptionHandler(AccessDeniedException.class)
    public AjaxResult<Void> handleAccessDeniedException(AccessDeniedException e, HttpServletRequest request)
    {
        log.error("********** 全局异常捕捉[AccessDeniedException] ********** {}", e.getMessage(), e);

        String requestURI = request.getRequestURI();
        log.error("请求地址'{}',权限校验失败'{}'", requestURI, e.getMessage());
        return AjaxResult.failed(HttpStatus.FORBIDDEN, "没有权限，请联系管理员授权");
    }

    /**
     * 请求路径中缺少必需的路径变量
     */
    @ExceptionHandler(MissingPathVariableException.class)
    public AjaxResult<Void> handleMissingPathVariableException(MissingPathVariableException e, HttpServletRequest request)
    {
        log.error("********** 全局异常捕捉[MissingPathVariableException] ********** {}", e.getMessage(), e);

        String requestURI = request.getRequestURI();
        log.error("请求路径中缺少必需的路径变量'{}',发生系统异常.", requestURI, e);

        return AjaxResult.failed(String.format("请求路径中缺少必需的路径变量[%s]", e.getVariableName()));
    }

    /**
     * 请求参数类型不匹配
     */
    @ExceptionHandler(MethodArgumentTypeMismatchException.class)
    public AjaxResult<Void> handleMethodArgumentTypeMismatchException(MethodArgumentTypeMismatchException e, HttpServletRequest request)
    {
        String requestURI = request.getRequestURI();
        String value = Convert.toStr(e.getValue());
        if (StringUtils.isNotEmpty(value))
        {
            value = EscapeUtil.clean(value);
        }
        log.error("请求参数类型不匹配'{}',发生系统异常.", requestURI, e);
        return AjaxResult.failed(String.format("请求参数类型不匹配，参数[%s]要求类型为：'%s'，但输入值为：'%s'", e.getName(), Objects.requireNonNull(e.getRequiredType()).getName(), value));
    }

    /**
     * 业务异常
     */
    @ExceptionHandler(ServiceException.class)
    public AjaxResult<Void> handleServiceException(ServiceException e)
    {
        log.error("********** 全局异常捕捉[ServiceException] ********** {}", e.getMessage(), e);

        Integer code = e.getCode();
        return StringUtils.isNotNull(code) ? AjaxResult.failed(code, e.getMessage()) : AjaxResult.failed(e.getMessage());
    }

    /**
     * 自定义验证异常
     */
    @ExceptionHandler(BindException.class)
    public AjaxResult<Void> handleBindException(BindException e)
    {
        log.error("********** 全局异常捕捉[BindException] ********** {}", e.getMessage(), e);

        log.error(e.getMessage(), e);
        String message = e.getAllErrors().get(0).getDefaultMessage();
        return AjaxResult.failed(message);
    }

    /**
     * 拦截未知的运行时异常
     */
    @ExceptionHandler(RuntimeException.class)
    public AjaxResult<Void> handleRuntimeException(RuntimeException e, HttpServletRequest request)
    {
        log.error("********** 全局异常捕捉[RuntimeException] ********** {}", e.getMessage(), e);

        String requestURI = request.getRequestURI();
        log.error("请求地址'{}',发生未知异常.", requestURI, e);
        return AjaxResult.failed(e.getMessage());
    }

    /**
     * 系统异常
     */
    @ExceptionHandler(value = Exception.class)
    public AjaxResult<Void> handleException(Exception e) {
        log.error("********** 全局异常捕捉[Exception] ********** {}", e.getMessage(), e);

        return AjaxResult.failed(HttpStatus.ERROR, "网络繁忙，请稍后重试！");
    }

}
